////////////////////////////第四章////////////////////////////////

#include <stdio.h>
#include <math.h>                       /* 程序中调用绝对值函数 fabs()，需包含 math.h */
#include<stdlib.h>
#include<time.h>

/* 【例4-1】使用格里高利公式求π的近似值，精度要求：最后一项的绝对值小于0.0001 */

int ex_4_1_pi(void)
{
	int denominator, flag;
	double item, pi;                     /* pi 用于存放累加和 */

										 /* 循环初始化 */
	flag = 1;                            /* flag 表示第 i 项的符号，初始为正 */
	denominator = 1;                     /* denominator表示第 i 项的分母，初始为1 */
	item = 1.0;                          /* item 中存放第 i 项的值，初值取 1 */
	pi = 0;                              /* 置累加和 pi 的初值为0 */

	while (fabs(item) >= 0.0001) {         /* 当|item| ≥ 0.0001时，执行循环 */
		item = flag * 1.0 / denominator; /* 计算第 i 项的值 */
		pi = pi + item;                  /* 累加第 i 项的值 */
		flag = -flag;                    /* 改变符号，为下一次循环做准备 */
		denominator = denominator + 2;   /* 分母递增2 ，为下一次循环做准备 */
	}
	pi = pi * 4;                         /* 循环计算的结果是 pi/4 */
	printf("pi = %.4f\n", pi);

	return 0;
}


/* 【例4-2】更改例3-3。从键盘输入一批学生的成绩，计算平均成绩，并统计不及格学生的人数。 */
int ex_4_2_average_point(void)
{
	int count, num;					/* num 记录输入的个数，count记录不及格学生人数 */
	double grade, total;			/* 分别存放成绩、成绩之和 */

	num = 0;
	total = 0;
	count = 0;
	printf("Enter grades: ");		/* 输入提示 */
	scanf("%lf", &grade);			/* 输入第一个数据，%lf中的l是字母 */

									/* 当输入数据 grad 大于等于0时，执行循环 */
	while (grade >= 0) {
		total = total + grade;		/* 累加成绩 */
		num++;						/* 计数 */
		if (grade < 60)
			count++;
		scanf("%lf", &grade);		/* 读入一个新数据，为下次循环做准备 */
	}
	if (num != 0) {
		printf("Grade average is %.2f\n", total / num);
		printf("Number of failures is %d\n", count);
	}
	else
		printf("Grade average is 0\n");

	return 0;
}

/* 【例4-3】从键盘读入一个整数，统计该数的位数。例如，输入12534，输出5；输入-99，输出2；输入0，输出1。*/
int ex_4_3_number_bits(void)
{
	int count, number;                  /* count 记录整数number的位数 */

	count = 0;
	printf("Enter a number:");          /* 输入提示 */
	scanf("%d", &number);
	if (number < 0) number = -number;    /* 将输入的负数转换为正数 */
	do {
		number = number / 10;           /* 整除后减少一位个位数，组成一个新数 */
		count++;                        /* 位数加1 */
	} while (number != 0);                /* 判断循环条件 */
	printf("It contains %d digits.\n", count);

	return 0;
}


/* 【例4-4-1】输入一个正整数m，判断它是否为素数。素数就是只能被1和自身整除的正整数，1不是素数，2是素数。*/
int ex_4_4_1_prime(void)
{
	int i, m;

	printf("Enter a number: ");  /* 输入提示 */
	scanf("%d", &m);
	for (i = 2; i <= m / 2; i++)  /* 第9行 */
		if (m % i == 0)
			break;               /* 若m能被某个i整除，则m不是素数，提前结束循环 */
	if (i > m / 2 && m != 1)      /* 若循环正常结束，说明m不能被任何一个i整除，则m是素数 */
		printf("%d is a prime number! \n", m);
	else
		printf("No!\n");         /* 第15行 */

	return 0;
}

/* 【例4-4-2】输入一个正整数m，判断它是否为素数。素数就是只能被1和自身整除的正整数，1不是素数，2是素数。*/
int ex_4_4_2_prime_2(void)
{
	int i, flag, m;

	flag = 1;
	printf("Enter a number: ");  /* 输入提示 */
	scanf("%d", &m);
	if (m == 1) flag = 0;
	for (i = 2; i <= m / 2; i++)
		if (m % i == 0) {
			flag = 0;
			break;               /* 若m能被某个i整除，则m不是素数，提前结束循环 */
		}

	if (flag == 1)
		printf("%d is a prime number! \n", m);
	else
		printf("No!\n");

	return 0;
}

/* 【例4-5-1】改进例3-1简单的猜数游戏。输入你所猜的正整数（假定1~100内），与计算机产生的被猜数比较，若相等，显示猜中；若不等，显示与被猜数的大小关系，最多允许猜7次。 */
int ex_4_5_1_guess_number_for(void)
{
	int count = 0, flag, mynumber, yournumber;

	mynumber = 38; /* 计算机产生的被猜数 */
	flag = 0;	/* flag的值为0表示没猜中，为1表示猜中了*/
	for (count = 1; count <= 7; count++) {			/* 最多能猜7次*/
		printf("Enter your number: ");       /* 提示输入你所猜的整数 */
		scanf("%d", &yournumber);
		if (yournumber == mynumber) {     /* 若相等，显示猜中 */
			printf("Lucky You!\n");
			flag = 1;
			break;                       /* 已猜中，终止循环 */
		}
		else
			if (yournumber>mynumber)
				printf("Too big\n");
			else
				printf("Too small\n");
	}
	if (flag == 0) 	/* 超过7次还没猜中，提示游戏结束*/
		printf("Game Over!\n");

	return 0;
}

/* 【例4-5-2】改进例3-1简单的猜数游戏。输入你所猜的正整数（假定1~100内），与计算机随机产生的被猜数比较，若相等，显示猜中；若不等，显示与被猜数的大小关系，最多允许猜7次。 */
int ex_4_5_2_guess_number_while(void)
{
	int count = 0, flag, mynumber, yournumber;
	srand(time(0));
	mynumber = rand() % 100 + 1; /* 计算机随机产生一个1~100之间的被猜数 */
	flag = 0;	/* flag的值为0表示没猜中，为1表示猜中了*/
	while (count < 7) {			/* 最多能猜7次*/
		printf("Enter your number: ");       /* 提示输入你所猜的整数 */
		scanf("%d", &yournumber);
		count++;
		if (yournumber == mynumber) {     /* 若相等，显示猜中 */
			printf("Lucky You!\n");
			flag = 1;
			break;                       /* 已猜中，终止循环 */
		}
		else
			if (yournumber>mynumber)
				printf("Too big\n");
			else
				printf("Too small\n");
	}
	if (flag == 0) 	/* 超过7次还没猜中，提示游戏结束*/
		printf("Game Over!\n");

	return 0;
}

/* 【例4-6】计算1! + 2! + 3! + … + 100!。要求定义和调用函数fact(n)计算n的阶乘。 */
double fact(int n)
{
	int i;
	double result;                     /* 变量 result 中存放阶乘的值 */

	result = 1;                        /* 置阶乘 result 的初值为1 */
	for (i = 1; i <= n; i++)            /* 循环执行n次，计算n! */
		result = result * i;

	return  result;                    /* 把结果回送主函数 */
}

int ex_4_6_factorial_sum_func(void)
{
	int i;
	double sum;

	sum = 0;
	for (i = 1; i <= 100; i++)
		sum = sum + fact(i);						/* 调用fact(i)求i!，共重复100次 */
	printf("1! + 2! + ... + 100! = %e\n", sum);		/* 用指数形式输出结果 */

	return 0;
}

/* 【例4-7】计算1! + 2! + 3! + … + 100!。要求使用嵌套循环 */
int ex_4_7_factorial_sum_2loop(void)
{
	int i, j;
	double item, sum;             /* 变量 item 中存放阶乘的值 */

	sum = 0;
	for (i = 1; i <= 100; i++) {    /* 外层循环执行100次，求累加和 */
		item = 1;				/* 置 item 的初值为1，以保证每次求阶乘都从1开始连乘 */
		for (j = 1; j <= i; j++)  /* 内层循环重复i次，算出item = i! */
			item = item * j;
		sum = sum + item;         /* 把 i! 累加到sum中 */
	}
	printf("1! + 2! + ... + 100! = %e\n", sum);   /* 用指数形式输出结果 */

	return 0;
}

/* 【例4-8】求最值问题。输入一批学生的成绩，找出最高分 */
int ex_4_8_highest_score(void)
{
	int i, mark, max, n;            /* max中放最高分 */

	printf("Enter n: ");            /* 输入提示 */
	scanf("%d", &n);                /* 输入数据个数 */
	printf("Enter %d marks: ", n);  /* 提示输入n个成绩 */
	scanf("%d", &mark);             /* 读入第一个成绩 */
	max = mark;          	        /* 假设第一个成绩是最高分 */
	for (i = 1; i < n; i++) {        /* 由于已经读了一个数，循环执行n-1次 */
		scanf("%d", &mark); 	    /* 读入下一个成绩 */
		if (max < mark)      	    /* 如果该成绩比最高分高 */
			max = mark;      	    /* 再假设该成绩为最高分 */
	}
	printf("Max = %d\n", max);

	return 0;
}

/* 【例4-9】逆序问题。输入一个正整数，将其逆序输出。例如，输入12345，输出54321。*/
int ex_4_9_reverse(void)
{
	int x;

	printf("Enter x: ");           /* 输入提示 */
	scanf("%d", &x);
	while (x != 0) {
		printf("%d ", x % 10);
		x = x / 10;
	}

	return 0;
}

/* 【例4-10】素数问题。求100以内的全部素数，每行输出10个。素数就是只能被1和自身整除的正整数，1不是素数，2是素数。 */
int ex_4_10_prime_in_100(void)
{
	int count, i;
	double m, n;

	count = 0;                      /* count记录素数的个数，用于控制输出格式 */
	for (m = 2; m <= 100; m++) {
		n = sqrt(m);
		for (i = 2; i <= n; i++)
			if ((int)m % i == 0)
				break;
		if (i > n) {              /* 如果m是素数 */
			printf("%6d", m);       /* 输出m */
			count++;                /* 累加已经输出的素数个数 */
			if (count % 10 == 0)     /* 如果count是10的倍数，换行 */
				printf("\n");
		}
	}
	printf("\n");

	return 0;
}

/* 【例4-11】菲波那契数列问题。输出菲波那契（Fibonacci）序列的前10项：1，1，2，3，5，8，13，21，34，55。 */
int ex_4_11_Fibonacci(void)
{
	int i, x1, x2, x;            /* x1和x2依次代表前两项，x表示其后一项 */

	x1 = 1;                      /* 头两项都是1 */
	x2 = 1;
	printf("%6d%6d", x1, x2);   /* 先输出头两项 */
	for (i = 1; i <= 8; i++) {     /* 循环输出后8项 */
		x = x1 + x2;             /* 计算新项 */
		printf("%6d", x);
		x1 = x2;                 /* 更新x1和x2，为下一次计算新项x作准备*/
		x2 = x;
	}
	printf("\n");

	return 0;
}

/* 【例4-12-1】穷举算法。（中国古典算术问题）某工地需要搬运砖块，已知男人一人搬3块，女人一人搬2块，小孩两人搬1块。有多少种搬法用45人正好搬45块砖？ */
int ex_4_12_1_move_brick_3for(void)
{
	int child, men, women;

	for (men = 0; men <= 45; men++)
		for (women = 0; women <= 45; women++)
			for (child = 0; child <= 45; child++)
				if (men + women + child == 45 && men * 3 + women * 2 + child*0.5 == 45)
					printf("men=%d,women=%d,child=%d\n", men, women, child);

	return 0;
}

/* 【例4-12-2】穷举算法。（中国古典算术问题）某工地需要搬运砖块，已知男人一人搬3块，女人一人搬2块，小孩两人搬1块。有多少种搬法用45人正好搬45块砖？ */
int ex_4_12_2_move_brick_2for(void)
{
	int child, women, men;

	for (men = 0; men <= 15; men++)
		for (women = 0; women <= 22; women++) {
			child = 45 - women - men;
			if (men * 3 + women * 2 + child * 0.5 == 45)
				printf("men=%d,women=%d,child=%d\n", men, women, child);
		}

	return 0;
}

//循环核心 = 每走一步找相同 + 特殊情况（即打断循环的2种）

/* 【例-循环的特殊打断，2种】 一个正整数m，判断它是否为素数。素数就是只能被1和自身整除的正整数，1不是素数，2是素数。*/
int loop_special2(void)
{
	int i, isPrime=1, m;  //isPrime=1 代表是素数，是个标记。

	printf("Enter a number: ");  /* 输入提示 */
	scanf("%d", &m);

	for (i = 1; i <= m-1; i++) {
		if(i == 1) {  //i等于1时，当前步跳过，继续后面的循环步骤。 特殊1：跳过当前步，继续后面循环
			continue;
		}
		if (m % i == 0) {
			isPrime = 0;
			break;    //若m能被某个i整除，则m不是素数，提前结束循环。 特殊2： 结束后面循环
		}
	}

	if(m==1){
		isPrime = 0;
	}

	if (isPrime == 1)
		printf("%d is a prime number! \n", m);
	else
		printf("%d is not a prime number! \n", m);

	return 0;
}

/*屏幕上显示1 2 3 4 
第1步：打印1 
第2步：打印2 
第3步：打印3
第4步：打印4  
每步做的都很像，都是打印当前步数。故：

四步
   { 打印当前步数 }

三种方法： 手动/for循环/while循环/do whle循环。李老师建议用for。
*/

int loop_3type()
{
	printf("%d", 1);
	printf("%d", 2);
	printf("%d", 3);
	printf("%d", 4);

	int i;
	for(i=1; i<=4; i++) {
		printf("%d", i);
	}

	i = 1;
	while(i<=4) {  //while后面括号里是条件成立时，就一直循环。
		printf("%d", i);
		i++;
	}

	i = 1;
	do{  //while后面括号里是条件成立时，就一直循环。
		printf("%d", i);
		i++;
	}while(i<=4);
	
	return 0;
}

int print10()
{
	for(int i=1;i<11;i++){

	printf("%d",i);
	
	}
    printf("\n");

	
	return 0;
}


int print1plus10()
{
	int sum=0;
	for(int i=1;i<11;i++){
    sum = sum+i;
	}
	printf("%d",sum);
    printf("\n");

	
	return 0;
}




int main(void)
{
	//loop_special2();
	//loop_3type();

	int choice, i;

	for (i = 1; i <= 20; i++) {                /* for 的循环体语句开始 */
		printf("Enter choice: ");             /* 输入提示 */
		scanf("%d", &choice);                 /* 接受用户输入的编号 */
											  /* 根据输入的编号，将相应的价格赋给price */
		switch (choice) {
			case 1: loop_special2(); break;
			case 2: loop_3type(); break;
			//case 41: ex_4_1_pi(); break;       /* 用break跳出switch语句，下同 */
			//case 42: ex_4_2_average_point(); break;       /* 用break跳出switch语句，下同 */
			//case 43: ex_4_3_number_bits(); break;
			case 441: ex_4_4_1_prime(); break;
			case 442: ex_4_4_2_prime_2(); break;
			//case 451: ex_4_5_1_guess_number_for(); break;
			//case 452: ex_4_5_2_guess_number_while(); break;
			case 46: ex_4_6_factorial_sum_func(); break;
			case 47: ex_4_7_factorial_sum_2loop(); break;       /* 用break跳出switch语句，下同 */
			//case 48: ex_4_8_highest_score(); break;       /* 用break跳出switch语句，下同 */
			//case 49: ex_4_9_reverse(); break;
			case 410: ex_4_10_prime_in_100(); break;
			//case 411: ex_4_11_Fibonacci(); break;
			//case 4121: ex_4_12_1_move_brick_3for(); break;
			//case 4122: ex_4_12_2_move_brick_2for(); break;
			case 413: print10(); break;
			case 414: print1plus10(); break;

			default: printf("err"); break;
		}
		/* 输出商品的价格 */
		printf("choice = %d end\n", choice);
	}                                        /* for 的循环体语句结束 */

	printf("Thanks \n");                     /* 结束查询，谢谢用户使用 */

	return 0;
}

//温习上面case 1和case 2
//循环练习1： 写一个函数，用for打印出12345678910，在上面switch里面增加一个case来调用它。
//结果正确后，提交至码云gitee。
//！！！注意： 老师已添加了，case 413: print10(), 你只用在它里面添加功能代码后，f10查看结果正确即可） 

//注意：后面练习步骤同上，结果正确后提交至码云。

//循环练习2： 同上步骤，写一个函数，打印出 1+2+3+4+5+...+10 的结果，在上面switch里面增加一个case来调用它。
//循环练习3： 写一个函数，用scanf输入一个数，打印出其阶乘，在上面switch里面增加一个case来调用它。
//循环练习4： 同上， 打印出1! + 2! + 3! + … + 100!  提示：调用上面函数来求每个数的阶乘
//循环练习5： 同上，模仿上面case 441: ex_4_4_1_prime()， 写新函数实现同样功能。
//循环练习6： 同上，模仿上面case 410: ex_4_10_prime_in_100， 写新函数打印出100以内的素数之和。



